
#include "n32g45x.h"
#include "board.h"

static volatile uint32_t _tick = 0;
uint32_t driv_tick_get(void)
{
    uint32_t primask = __get_PRIMASK();
    __disable_irq();
    uint32_t tick = _tick;
    __set_PRIMASK(primask);

    return tick;
}

void driv_tick_inc(void)
{
    _tick ++;
}

uint32_t driv_tick_elaps(uint32_t prev_tick)
{
    uint32_t act_time = driv_tick_get();

    if(act_time >= prev_tick) {
        prev_tick = act_time - prev_tick;
    }
    else {
        prev_tick = UINT32_MAX - prev_tick + 1;
        prev_tick += act_time;
    }
    return prev_tick;
}

int hw_delay_ms(uint32_t ms)
{
    uint32_t t = driv_tick_get();
    while(driv_tick_elaps(t) < ms) {
        /* Do something to no call `ev_tick_get` too often,
           as it interfere with interrupts
        */
        volatile uint32_t i;
        volatile uint32_t x = ms;
        for(i = 0; i < 200; i++) {
            x = x * 3;
        }
    }
    return 0;
}

const char *driv_get_board(void)
{
    return "N32G45XVL_STB";
}

static void peripheral_clock_op(FunctionalState state)
{
    // AHB
    RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA1 | RCC_AHB_PERIPH_ADC1, state);

    // ADC
    RCC_ConfigAdc1mClk(RCC_ADC1MCLK_SRC_HSE, RCC_ADC1MCLK_DIV8);

    // APB2
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO, state);
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA | RCC_APB2_PERIPH_GPIOB |
                            RCC_APB2_PERIPH_GPIOC | RCC_APB2_PERIPH_GPIOD |
                            RCC_APB2_PERIPH_GPIOE, state);
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_USART1, state);
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_TIM1, state);

    // APB1
    RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_TIM7, state); // for hw_delay
    RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_PWR, state);
    RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_USART2, state);

}

static void gpio_init(void)
{
    GPIO_InitType GPIO_InitStructure;
    GPIO_InitStruct(&GPIO_InitStructure);
    // uart1 tx
    GPIO_InitStructure.Pin        = GPIO_PIN_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);

    // uart1 rx
    GPIO_InitStructure.Pin       = GPIO_PIN_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);

    // run led
    GPIO_InitStructure.Pin        = GPIO_PIN_5;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);

    // adc in
    GPIO_InitStructure.Pin  = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_InitPeripheral(GPIOC, &GPIO_InitStructure);

    // uart2 tx
    GPIO_InitStructure.Pin        = GPIO_PIN_2;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);

    // uart2 rx
    GPIO_InitStructure.Pin       = GPIO_PIN_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);

}

/* jump to application */

typedef void (*app_func_t)(void);
void driv_jump_to_app(uint32_t address)
{
    uint32_t app_addr = *(uint32_t *)(address + 4);
    app_func_t app = (app_func_t)app_addr;
    __set_MSP(*(uint32_t*)address);
    app();
}

/* reboot */

static volatile uint32_t reboot_ms = 0;
void driv_cpu_reboot(uint32_t ms)
{
    if (ms == 0) {
        NVIC_SystemReset();
        while(1);
    }
    uint32_t primask = __get_PRIMASK();
    __disable_irq();
    reboot_ms = ms;
    __set_PRIMASK(primask);
}

void driv_cpu_reboot_check(void)
{
    if (reboot_ms > 0) {
        reboot_ms --;
        if (reboot_ms == 0) {
            NVIC_SystemReset();
            while(1);
        }
    }
}

int driv_board_init(int is_app)
{
    if(is_app) {
        SCB->VTOR = APP1_BASE;
    } else {
        SCB->VTOR = BOOT_BASE;
    }
    SysTick_Config(SystemCoreClock / 1000);
    peripheral_clock_op(ENABLE);
    gpio_init();
    return 0;
}

int driv_board_deinit(int is_app)
{
    (void)is_app;
    __disable_irq();
    SysTick->CTRL = 0;
    peripheral_clock_op(DISABLE);
    return 0;
}

void driv_enable_irq(uint32_t flag)
{
    __set_PRIMASK(flag);
}
uint32_t driv_disable_irq(void)
{
    uint32_t flag = __get_PRIMASK();
    __disable_irq();
    return flag;
}

/* assert */

#ifdef  USE_FULL_ASSERT
void assert_failed(const uint8_t* expr, const uint8_t* file, uint32_t line)
{
    (void)expr;
    (void)file;
    (void)line;
    while (1)
    {
    }
}
#endif /* USE_FULL_ASSERT */


